package com.ant.backstage.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.ant.backstage.dao.GoodsActivityMapper;
import com.ant.backstage.dao.GoodsMapper;
import com.ant.backstage.facade.ApiReturn;
import com.ant.backstage.facade.code.ApiReturnCode;
import com.ant.backstage.facade.dto.GoodsActivityDTO;
import com.ant.backstage.facade.enums.ActivityTypeEnum;
import com.ant.backstage.facade.model.ActivitySuccessLog;
import com.ant.backstage.facade.model.Goods;
import com.ant.backstage.facade.model.GoodsActivity;
import com.ant.backstage.facade.model.User;
import com.ant.backstage.facade.request.activity.SpikeGoodsActivityRequest;
import com.ant.backstage.service.ActivityService;
import com.ant.backstage.service.common.SpikeGoodsQueueThread;
import com.ant.backstage.utils.RedisUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @date 2022/2/13 14:17
 **/
@Service("activityService")
public class ActivityServiceImpl implements ActivityService {

    private static Logger logger = LogManager.getLogger(ActivityServiceImpl.class);

    private static LinkedBlockingDeque<ActivitySuccessLog> deque = new LinkedBlockingDeque<>();

    @Resource
    private GoodsActivityMapper goodsActivityMapper;

    @Resource
    private GoodsMapper goodsMapper;
    @Resource
    private RedisUtil redisUtil;


    @Override
    public ApiReturn InitializeSpikeData() {
        ApiReturn apiReturn = new ApiReturn();
        //秒杀活动数据初始化
        GoodsActivityDTO activityDTO = new GoodsActivityDTO();
        //活动名称 正常配置在配置中心
        activityDTO.setActivityname("商品秒杀");
        activityDTO.setActivitytype(ActivityTypeEnum.GOODS_SPIKE.getCode());
        activityDTO.setCurrentTime(new Date());
        List<GoodsActivity> goodsActivities = goodsActivityMapper.selectByType(activityDTO);
        // Optional不适合集合的判空处理
        if (goodsActivities == null || goodsActivities.size() == 0) {
            logger.error("没有商品参加秒杀活动！！！");
            apiReturn.setCode(ApiReturnCode.RESOURCE_IS_NOT.getCode());
            apiReturn.setMsg(ApiReturnCode.RESOURCE_IS_NOT.getMessage());
            return apiReturn;
        }
        List<Integer> goodsIds = goodsActivities.stream().filter(a -> a.getGoodsstock() > 0).map(GoodsActivity::getGoodsid).distinct().collect(Collectors.toList());
        //redis 的过期时间 活动结束后一小时
        List<Date> endTimeList = goodsActivities.stream().map(GoodsActivity::getActivityendtime).collect(Collectors.toList());
        List<Date> startTimeList = goodsActivities.stream().map(GoodsActivity::getActivitystarttime).collect(Collectors.toList());
        Date maxDate = Collections.max(endTimeList);
        Date minDate = Collections.min(startTimeList);
        //求秒
        long nowDate = System.currentTimeMillis();
        long endTime = maxDate.getTime();
        int redisTime = (int) ((nowDate - endTime) / 1000) + 60 * 60 * 60;

        List<Goods> goods = goodsMapper.selectByIds(goodsIds);
        String redisKey = "ACTIVITY::UID_" + goodsActivities.get(0).getActivityuid() + "::TYPE_" + ActivityTypeEnum.GOODS_SPIKE.getCode();
        goods.stream().forEach(good -> {
            goodsActivities.forEach(a -> {
                if (good.getId().equals(a.getGoodsid())) {
                    //校验参数
                    /*
                       1. 商品库存要大于参加活动的库存
                       2. 商品已上架 并且已审核通过
                    */
                    if (a.getGoodsstock() >= good.getGoodsstock()) {
                        logger.error("参加活动的库存不足，请及时补充goodsActivities={}good={}",
                                JSONObject.toJSONString(a), JSONObject.toJSONString(good));
                        return;
                    }
                    if (!String.valueOf(good.getGoodsstatus()).equals("1") || !String.valueOf(good.getDataflag()).equals("1")) {
                        logger.error("商品无效 good={}", JSONObject.toJSONString(good));
                        return;
                    }
                    //商品编号唯一
                    redisUtil.hset(redisKey, good.getGoodssn(), a.getGoodsstock(), redisTime);
                }
            });
        });
        if (redisUtil.hasKey(redisKey)) {
            redisUtil.hset(redisKey, "activityStartTime", minDate, redisTime);
            redisUtil.hset(redisKey, "activityEndTime", maxDate, redisTime);
        }
        //todo 静态页面的初始化 根据模板生成静态页面
        apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
        return apiReturn;
    }

    @Override
    public ApiReturn spikeGoodsActivity(String activityKey, String successKey, SpikeGoodsActivityRequest spikeGoodsActivityRequest, User user) {
        ApiReturn apiReturn = new ApiReturn();

        //redis分布式锁 保证正确预减库存  lua+redis
        List<String> keyList = new ArrayList<>();
        keyList.add(user.getUid());
        keyList.add(spikeGoodsActivityRequest.getGoodsScn());
        keyList.add(successKey);
        keyList.add(activityKey);
        /**
         *  @user_script:22: user_script:22: attempt to compare nil with number
         * redis HashValue 序列化问题 需要使用json序列化
         **/
        Integer result = redisUtil.runLuaScript("spikeGoods.lua", keyList);
        logger.info("秒杀活动 lua 脚本执行完毕result={}", JSONObject.toJSONString(result));
        switch (result) {
            case 1:
                logger.info("秒杀成功");
                apiReturn.setCode(ApiReturnCode.ACTIVITY_SPIKE_GOODS_SUCCESS.getCode());
                apiReturn.setMsg(ApiReturnCode.ACTIVITY_SPIKE_GOODS_SUCCESS.getMessage());
                // 成功记录放入队列，开启异步处理，持久化成功记录，下单业务
                boolean b = redisUtil.hHasKey(successKey, user.getUid());
                if (!b) {
                    apiReturn.setCode(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getCode());
                    apiReturn.setMsg(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getMessage());
                    break;
                }
                Object hget = redisUtil.hget(successKey, user.getUid());
                ActivitySuccessLog activitySuccessLog = new ActivitySuccessLog();
                activitySuccessLog.setActivityId(spikeGoodsActivityRequest.getActivityUid());
                activitySuccessLog.setAmount(1);
                activitySuccessLog.setSuccessTime(new Date());
                activitySuccessLog.setGoodsScn((String) hget);
                activitySuccessLog.setCreatetime(new Date());
                activitySuccessLog.setUserUid(user.getUid());
                //异步多线程 CompletableFuture
                if (deque.remainingCapacity() > 0) {
                    //放入队列
                    deque.add(activitySuccessLog);
                }
                SpikeGoodsQueueThread saveUserLogQueueRunnable = new SpikeGoodsQueueThread(deque);
                //单一线程  todo 优化为多线程 添加线程池
                Executors.newSingleThreadScheduledExecutor().submit(saveUserLogQueueRunnable);

                break;
            case -1:
                logger.error("当前用户已经存在秒杀记录");
                apiReturn.setCode(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getCode());
                apiReturn.setMsg(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getMessage());
                break;
            case -2:
                logger.error("缓存中不存在活动商品库存，活动未开始或参数设置异常");
                apiReturn.setCode(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getCode());
                apiReturn.setMsg(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getMessage());
                break;
            case 0:
                logger.error("秒杀库存为零");
                apiReturn.setCode(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getCode());
                apiReturn.setMsg(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getMessage());
                break;
            default:
                logger.error("默认秒杀失败");
                apiReturn.setCode(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getCode());
                apiReturn.setMsg(ApiReturnCode.ACTIVITY_SPIKE_GOODS.getMessage());
                break;
        }
        return apiReturn;
    }
}
